#!/usr/bin/env python3
"""
full_story_douyin_video — 综合视频合成脚本
生成所有视觉场景并合成最终视频
"""

import os
import sys
import subprocess
import json
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import numpy as np
import math

# ── 路径配置 ──
BASE = r"E:\集群文件夹\factory_os\short_video_real_data_pipeline\phase4b_90s_formal_sample\v22_kimi_claude_loop\full_story_douyin_video"
FFMPEG = r"D:\AI_WORKSPACE\tools\ffmpeg\ffmpeg.exe"
FFPROBE = r"D:\AI_WORKSPACE\tools\ffmpeg\ffprobe.exe"

CLIPS_DIR = os.path.join(BASE, "05_clips")
VIDEO_DIR = os.path.join(BASE, "06_video")
ASSETS_DIR = os.path.join(BASE, "03_assets")
VO_DIR = os.path.join(BASE, "02_voiceover")
SUBS_DIR = os.path.join(BASE, "04_subtitles")

V9_VIDEO = r"E:\集群文件夹\factory_os\short_video_real_data_pipeline\phase4b_90s_formal_sample\v22_kimi_claude_loop\claude_ui_rebuild\final_release_package_claude_ui_v9_best\video\v22_claude_code_ui_rebuild_multi_agent_v9_kimi_cleanup.mp4"

os.makedirs(CLIPS_DIR, exist_ok=True)
os.makedirs(VIDEO_DIR, exist_ok=True)
os.makedirs(ASSETS_DIR, exist_ok=True)

W, H = 1080, 1920
FPS = 30

# ══════════════════════════════════════════════
# 场景定义
# ══════════════════════════════════════════════

SCENES = [
    # (id, start_sec, duration, type, params)
    ("title", 0, 3.5, "title_card", {"text": "我让 Claude Code\n自己做视频"}),
    ("punch", 3.5, 3, "punch_montage", {}),
    ("recording_fail", 6.5, 4, "terminal", {"lines": [
        "$ ffmpeg -f gdigrab -i desktop out.mp4",
        "[gdigrab] 无法捕获桌面: 会话隔离",
        "$ ls -la output.mp4",
        "-rw-r--r--  0 output.mp4  ←  RED",
        "× 录屏失败 — Session 0/1 隔离"
    ]}),
    ("five_fails", 10.5, 4, "fail_list", {}),
    ("ai_drift", 14.5, 4, "terminal", {"lines": [
        "→ 录屏方案全部失败",
        "→ 让我看看 Amazon 选品...",
        "→ Sorftime 市场分析",
        "→ 卖家精灵关键词采集",
        "⚠ 方向已偏离!"
    ]}),
    ("chatgpt_fix", 18.5, 4, "chatgpt_card", {}),
    ("python_render", 22.5, 4, "code_screen", {}),
    ("multi_agent", 26.5, 4, "agent_cards", {}),
    ("log_lines", 30.5, 3, "terminal", {"lines": [
        "[Bash] pip install torch...",
        "[Write] render_frames_v9.py",
        "[Edit]  compose_v9.py:51",
        "[Agent] researcher finished"
    ]}),
    ("kimi_intro", 33.5, 4, "kimi_review", {}),
    ("score_bars", 37.5, 6, "score_chart", {}),
    ("v9_peak", 43.5, 3, "score_highlight", {"version": "v9", "score": 92}),
    ("v10_crash", 46.5, 5, "crash_scene", {}),
    ("v11_recovery", 51.5, 4, "recovery_scene", {}),
    ("lesson", 55.5, 3, "fullscreen_text", {"text": "一个硬编码 bug\n能让你一夜回到解放前"}),
    ("rollback", 58.5, 3, "rollback_scene", {}),
    ("review_portal", 61.5, 4, "portal_scene", {}),
    ("http_verify", 65.5, 4, "http_verify_scene", {}),
    ("summary", 69.5, 4, "summary_cards", {}),
    ("final_montage", 73.5, 4, "final_montage", {}),
    ("end_card", 77.5, 7.5, "end_card", {}),
]


def create_title_card(text, output_path):
    """创建标题卡片 — 黑底白字"""
    img = Image.new('RGB', (W, H), (10, 10, 26))
    draw = ImageDraw.Draw(img)
    # Try to use a font
    try:
        font_large = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 72)
    except:
        font_large = ImageFont.load_default()

    lines = text.split('\n')
    total_h = len(lines) * 90
    y_start = (H - total_h) // 2

    for i, line in enumerate(lines):
        bbox = draw.textbbox((0, 0), line, font=font_large)
        tw = bbox[2] - bbox[0]
        x = (W - tw) // 2
        y = y_start + i * 90
        # Draw with stroke
        for dx, dy in [(-2,-2),(-2,2),(2,-2),(2,2),(0,-2),(0,2),(-2,0),(2,0)]:
            draw.text((x+dx, y+dy), line, fill=(30, 30, 50), font=font_large)
        draw.text((x, y), line, fill=(255, 255, 255), font=font_large)

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_terminal_screen(lines, output_path):
    """创建终端屏幕"""
    img = Image.new('RGB', (W, H), (12, 12, 30))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/consola.ttf", 32)
        font_bold = ImageFont.truetype("C:/Windows/Fonts/consolab.ttf", 32)
    except:
        font = ImageFont.load_default()
        font_bold = font

    # Terminal frame
    margin = 60
    draw.rectangle([margin, 120, W-margin, H-120], fill=(18, 18, 40), outline=(60, 60, 100))

    # Title bar
    draw.rectangle([margin, 120, W-margin, 170], fill=(40, 40, 70))
    draw.text((margin+20, 130), "Terminal — Claude Code", fill=(180, 180, 200), font=font)

    y = 200
    for line in lines:
        if '← RED' in line:
            text = line.replace(' ← RED', '')
            draw.text((margin+20, y), text, fill=(255, 60, 60), font=font)
        elif '⚠' in line:
            draw.text((margin+20, y), line, fill=(255, 180, 0), font=font_bold)
        elif line.startswith('$ '):
            draw.text((margin+20, y), line, fill=(0, 220, 100), font=font)
        else:
            draw.text((margin+20, y), line, fill=(200, 200, 210), font=font)
        y += 45

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_agent_cards(output_path):
    """创建多 Agent 并行卡片"""
    img = Image.new('RGB', (W, H), (12, 12, 30))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 28)
        font_bold = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 30)
    except:
        font = ImageFont.load_default()
        font_bold = font

    agents = [
        ("🧠 analyst", "分析需求", 80, (0, 200, 255)),
        ("🔍 researcher", "搜索方案", 60, (100, 200, 100)),
        ("💻 coder", "编写代码", 40, (255, 200, 50)),
        ("✅ reviewer", "审核中", 20, (200, 100, 255)),
    ]

    card_w, card_h = 800, 200
    start_y = (H - (len(agents) * (card_h + 20))) // 2

    for i, (name, task, pct, color) in enumerate(agents):
        y = start_y + i * (card_h + 20)
        x = (W - card_w) // 2

        # Card background
        draw.rounded_rectangle([x, y, x+card_w, y+card_h], radius=16, fill=(22, 22, 50), outline=(60, 60, 100))

        # Agent name
        draw.text((x+30, y+15), name, fill=(255, 255, 255), font=font_bold)
        draw.text((x+30, y+55), task, fill=(150, 150, 180), font=font)

        # Progress bar background
        bar_x, bar_y = x+30, y+95
        bar_w, bar_h = card_w-60, 24
        draw.rounded_rectangle([bar_x, bar_y, bar_x+bar_w, bar_y+bar_h], radius=12, fill=(40, 40, 70))

        # Progress bar fill
        fill_w = int(bar_w * pct / 100)
        draw.rounded_rectangle([bar_x, bar_y, bar_x+fill_w, bar_y+bar_h], radius=12, fill=color)

        # Percentage text
        draw.text((x+30, y+130), f"{pct}%", fill=color, font=font_bold)

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_chatgpt_card(output_path):
    """创建 ChatGPT 纠偏对话框"""
    img = Image.new('RGB', (W, H), (12, 12, 30))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 28)
        font_bold = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 32)
    except:
        font = ImageFont.load_default()
        font_bold = font

    # Dialog box
    box_w, box_h = 900, 500
    x = (W - box_w) // 2
    y = (H - box_h) // 2 - 60

    draw.rounded_rectangle([x, y, x+box_w, y+box_h], radius=20, fill=(35, 35, 65), outline=(0, 180, 255), width=3)

    # Header
    draw.text((x+30, y+20), "💬 ChatGPT", fill=(0, 180, 255), font=font_bold)

    # Messages
    messages = [
        ("不要尝试录屏了。", False),
        ("Claude Code 本身就是一个 UI 引擎。", False),
        ("让它自己画自己的界面——用 Python。", True),
        ("用真实执行日志驱动画面。", True),
    ]
    my = y + 80
    for msg, highlight in messages:
        if highlight:
            draw.text((x+30, my), msg, fill=(255, 215, 0), font=font_bold)
        else:
            draw.text((x+30, my), msg, fill=(200, 200, 210), font=font)
        my += 50

    # Key instruction box
    key_y = y + 310
    draw.rounded_rectangle([x+30, key_y, x+box_w-30, key_y+90], radius=12, fill=(50, 50, 80), outline=(255, 215, 0), width=2)
    draw.text((x+50, key_y+20), "放弃录屏 → 程序化 UI 重构", fill=(255, 215, 0), font=font_bold)

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_code_screen(output_path):
    """创建代码编辑器画面"""
    img = Image.new('RGB', (W, H), (18, 18, 35))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/consola.ttf", 30)
    except:
        font = ImageFont.load_default()

    code_lines = [
        ("  1", "def render_terminal(scene):"),
        ("  2", "    draw_tool_call('Bash()')"),
        ("  3", "    draw_agent_status(agents)"),
        ("  4", "    draw_progress_bar(percent)"),
        ("  5", ""),
        ("  6", "for v in versions:"),
        ("  7", "    render(v)"),
        ("  8", "    kimi_score = kimi_audit(v)"),
        ("  9", "    if kimi_score < target:"),
        (" 10", "        auto_fix(kimi_report)"),
    ]

    colors = {
        'def': (0, 200, 255), 'draw_tool_call': (255, 200, 100),
        'draw_agent_status': (255, 200, 100), 'draw_progress_bar': (255, 200, 100),
        'for': (200, 100, 255), 'in': (200, 100, 255),
        'if': (200, 100, 255), 'render': (100, 200, 100),
        'kimi_audit': (100, 200, 100), 'auto_fix': (100, 200, 100),
        'kimi_score': (255, 215, 0), 'kimi_report': (255, 215, 0),
    }

    # Editor frame
    margin = 40
    draw.rectangle([margin, 100, W-margin, H-100], fill=(22, 22, 45), outline=(50, 50, 80))

    # Line numbers
    y = 150
    for num, code in code_lines:
        # Line number
        draw.text((margin+20, y), num, fill=(80, 80, 100), font=font)
        # Code
        draw.text((margin+100, y), code, fill=(180, 200, 180), font=font)
        y += 48

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_score_chart(output_path):
    """创建评分柱状图"""
    img = Image.new('RGB', (W, H), (10, 10, 26))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 26)
        font_bold = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 36)
        font_label = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 28)
    except:
        font = ImageFont.load_default()
        font_bold = font
        font_label = font

    scores = [
        ("v1", 69, (120, 120, 140)),
        ("v2", 72, (100, 160, 100)),
        ("v3", 81, (80, 200, 80)),
        ("v4", 85, (60, 220, 60)),
        ("v5", 86, (60, 220, 60)),
        ("v6", 89, (40, 240, 40)),
        ("v7", 90, (40, 240, 40)),
        ("v9", 92, (255, 215, 0)),
    ]

    # Chart area
    chart_x, chart_w = 120, 840
    chart_y, chart_h = 450, 900
    max_score = 100

    draw.text((W//2, 120), "Kimi 评分迭代历程", fill=(200, 200, 220), font=font_bold, anchor="mt")
    draw.text((W//2, 170), "69 → 72 → 81 → 85 → 86 → 89 → 90 → 92", fill=(100, 100, 140), font=font, anchor="mt")

    # Draw bars
    bar_w = 75
    gap = 20
    total_w = len(scores) * (bar_w + gap) - gap
    start_x = (W - total_w) // 2

    for i, (label, score, color) in enumerate(scores):
        x = start_x + i * (bar_w + gap)
        bar_h_val = int((score / max_score) * (chart_h - 60))
        y = chart_y + chart_h - bar_h_val - 60

        # Bar
        draw.rounded_rectangle([x, y, x+bar_w, y+bar_h_val+60], radius=8, fill=color)

        # Score label
        draw.text((x+bar_w//2, y-10), str(score), fill=color, font=font_bold, anchor="mb")

        # Version label
        draw.text((x+bar_w//2, chart_y+chart_h-20), label, fill=(150, 150, 180), font=font_label, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_crash_scene(output_path):
    """V10 崩溃场景"""
    img = Image.new('RGB', (W, H), (40, 5, 5))
    draw = ImageDraw.Draw(img)
    try:
        font_big = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 100)
        font = ImageFont.truetype("C:/Windows/Fonts/consola.ttf", 28)
    except:
        font_big = ImageFont.load_default()
        font = font_big

    # Score drop
    draw.text((W//2, 300), "92 → 76", fill=(255, 50, 50), font=font_big, anchor="mt")

    # Explanation
    draw.text((W//2, 450), "💥 REGRESSION", fill=(255, 100, 100), font=font_big, anchor="mt")

    # Bug code
    code_y = 650
    draw.text((W//2, code_y), 'render_frames_v10.py:588', fill=(180, 80, 80), font=font, anchor="mt")
    draw.text((W//2, code_y+60), 'status = "3/3 agents working"  ← 硬编码', fill=(255, 100, 100), font=font, anchor="mt")
    draw.text((W//2, code_y+120), '画面显示 4 agents 但底部只显示 3/3', fill=(200, 100, 100), font=font, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_rollback_scene(output_path):
    """回滚决策场景"""
    img = Image.new('RGB', (W, H), (8, 20, 8))
    draw = ImageDraw.Draw(img)
    try:
        font_big = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 42)
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 32)
    except:
        font_big = ImageFont.load_default()
        font = font_big

    decisions = [
        ("❌ 保留 V11 继续", "88 < 92 — 不通过", (200, 80, 80)),
        ("❌ 从 V10 重来", "Regression 路线已死", (200, 80, 80)),
        ("✅ 回滚 V9", "92/100 🏆 → FROZEN", (80, 220, 80)),
    ]

    y = 500
    for title, desc, color in decisions:
        draw.text((200, y), title, fill=color, font=font_big)
        draw.text((200, y+50), desc, fill=(180, 180, 200), font=font)
        y += 150

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_portal_scene(output_path):
    """Review Portal 场景"""
    img = Image.new('RGB', (W, H), (20, 20, 40))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/consola.ttf", 28)
        font_bold = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 32)
    except:
        font = ImageFont.load_default()
        font_bold = font

    # Browser frame
    draw.rectangle([40, 120, W-40, H-120], fill=(30, 30, 55), outline=(80, 80, 120))
    draw.rectangle([40, 120, W-40, 170], fill=(50, 50, 75), outline=(80, 80, 120))
    draw.text((60, 135), "🌐 review-portal-773.pages.dev", fill=(100, 200, 255), font=font)

    # URL bar
    draw.rounded_rectangle([100, 195, W-100, 235], radius=8, fill=(40, 40, 65))
    draw.text((120, 205), "https://review-portal-773.pages.dev/ → 200 OK ✅", fill=(100, 220, 100), font=font)

    # Content
    content_items = [
        "📋 Review Portal",
        "  ├── runs/",
        "  │   └── claude-ui-rebuild_v9-best-final/",
        "  │       ├── index.html → 200 ✅",
        "  │       ├── videos/v9.mp4 → 200 ✅",
        "  │       └── manifest.json → 200 ✅",
        "  └── latest.json → 200 ✅",
    ]
    y = 290
    for item in content_items:
        if "200" in item and "✅" in item:
            draw.text((80, y), item, fill=(100, 220, 100), font=font)
        else:
            draw.text((80, y), item, fill=(180, 180, 200), font=font)
        y += 45

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_http_verify(output_path):
    """HTTP 验证场景"""
    img = Image.new('RGB', (W, H), (10, 10, 26))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/consola.ttf", 30)
    except:
        font = ImageFont.load_default()

    verifications = [
        ("$ curl -I home", "HTTP/1.1 200 OK 💚"),
        ("$ curl -I v9 run", "HTTP/1.1 200 OK 💚"),
        ("$ curl -I v9 video", "HTTP/1.1 200 OK 💚"),
        ("$ curl -I manifest", "HTTP/1.1 200 OK 💚"),
        ("$ curl -I latest.json", "HTTP/1.1 200 OK 💚"),
    ]

    y = 500
    for cmd, result in verifications:
        draw.text((100, y), cmd, fill=(0, 200, 100), font=font)
        draw.text((100, y+50), result, fill=(100, 255, 100), font=font)
        y += 130

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_summary_cards(output_path):
    """统计卡片"""
    img = Image.new('RGB', (W, H), (10, 10, 26))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 48)
        font_num = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 72)
    except:
        font = ImageFont.load_default()
        font_num = font

    stats = [
        ("11", "轮迭代"),
        ("1", "次 Regression"),
        ("+23", "总提升"),
        ("92", "最终分 🏆"),
    ]

    card_w, card_h = 400, 350
    gap = 40
    total_w = 2 * card_w + gap
    start_x = (W - total_w) // 2
    start_y = (H - 2 * card_h - gap) // 2

    for i, (num, label) in enumerate(stats):
        col = i % 2
        row = i // 2
        x = start_x + col * (card_w + gap)
        y = start_y + row * (card_h + gap)

        draw.rounded_rectangle([x, y, x+card_w, y+card_h], radius=20, fill=(22, 22, 50), outline=(60, 60, 100))
        draw.text((x+card_w//2, y+60), num, fill=(0, 200, 255), font=font_num, anchor="mt")
        draw.text((x+card_w//2, y+200), label, fill=(180, 180, 200), font=font, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_punch_montage(output_path):
    """三画面蒙太奇"""
    img = Image.new('RGB', (W, H), (10, 10, 26))
    draw = ImageDraw.Draw(img)
    try:
        font_big = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 48)
        font = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 36)
    except:
        font_big = ImageFont.load_default()
        font = font_big

    panels = [
        ("❌ 录屏失败", (200, 60, 60)),
        ("⚠ AI 跑偏", (200, 120, 20)),
        ("69 → 92 🏆", (255, 215, 0)),
    ]

    panel_w, panel_h = 900, 350
    start_y = (H - (3 * panel_h + 2 * 20)) // 2

    for i, (text, color) in enumerate(panels):
        y = start_y + i * (panel_h + 20)
        x = (W - panel_w) // 2
        draw.rounded_rectangle([x, y, x+panel_w, y+panel_h], radius=16, fill=(22, 22, 50), outline=color, width=3)
        draw.text((W//2, y+panel_h//2), text, fill=color, font=font_big, anchor="mm")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_fail_list(output_path):
    """五种失败方案"""
    img = Image.new('RGB', (W, H), (30, 5, 5))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 28)
    except:
        font = ImageFont.load_default()

    fails = [
        "✗ Direct bash gdigrab — 空/2秒",
        "✗ Task Scheduler /IT — 5段静态",
        "✗ WScript/VBS — 无法录屏",
        "✗ XML InteractiveToken — 相同画面",
        "✗ ASCII 路径 — 依然失败",
    ]

    y = 600
    for fail in fails:
        draw.text((200, y), fail, fill=(200, 80, 80), font=font)
        y += 65

    draw.text((540, 1350), "桌面会话隔离: Session 0 ≠ Session 1", fill=(150, 60, 60), font=font)

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_kimi_review(output_path):
    """Kimi 审核画面"""
    img = Image.new('RGB', (W, H), (15, 15, 35))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 26)
        font_bold = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 32)
    except:
        font = ImageFont.load_default()
        font_bold = font

    draw.text((W//2, 120), "📋 Kimi 视觉审核 v1", fill=(0, 180, 255), font=font_bold, anchor="mt")

    items = [
        ("开头3秒吸引力", 7, (200, 200, 100)),
        ("界面真实感", 6, (200, 80, 80)),
        ("多Agent并行感", 5, (200, 60, 60)),
        ("PPT感消除", 5, (200, 80, 80)),
        ("信息节奏", 6, (200, 120, 80)),
        ("总分", 69, (255, 100, 100)),
    ]

    y = 200
    bar_x, bar_w = 500, 400
    for name, score, color in items:
        draw.text((100, y), name, fill=(180, 180, 200), font=font)
        fill_w = int(bar_w * score / 100)
        draw.rounded_rectangle([bar_x, y+5, bar_x+bar_w, y+35], radius=6, fill=(40, 40, 70))
        draw.rounded_rectangle([bar_x, y+5, bar_x+fill_w, y+35], radius=6, fill=color)
        draw.text((bar_x+bar_w+20, y+5), str(score), fill=color, font=font_bold)
        y += 55

    # Big score
    draw.text((W//2, 900), "69/100", fill=(255, 100, 100), font=ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 80), anchor="mt")
    draw.text((W//2, 1000), "❌ 未通过", fill=(200, 80, 80), font=font_bold, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_score_highlight(version, score, output_path):
    """单个高分高亮"""
    img = Image.new('RGB', (W, H), (10, 10, 26))
    draw = ImageDraw.Draw(img)
    try:
        font_big = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 120)
        font = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 36)
    except:
        font_big = ImageFont.load_default()
        font = font_big

    draw.text((W//2, 600), f"{version.upper()}", fill=(255, 215, 0), font=font, anchor="mt")
    draw.text((W//2, 700), f"{score}/100 🏆", fill=(255, 215, 0), font=font_big, anchor="mt")
    draw.text((W//2, 900), "可直接发布 ✅", fill=(100, 255, 100), font=font, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_recovery_scene(output_path):
    """V11 恢复场景"""
    img = Image.new('RGB', (W, H), (20, 15, 5))
    draw = ImageDraw.Draw(img)
    try:
        font_big = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 80)
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 36)
    except:
        font_big = ImageFont.load_default()
        font = font_big

    draw.text((W//2, 400), "76 → 88", fill=(255, 200, 50), font=font_big, anchor="mt")
    draw.text((W//2, 520), "修复 6 项问题", fill=(180, 180, 200), font=font, anchor="mt")

    fixes = ["✅ Agent 动态计数", "✅ 黑屏间隙优化", "✅ Stagger 节奏", "✅ 底部文字清理"]
    y = 650
    for fix in fixes:
        draw.text((250, y), fix, fill=(120, 220, 120), font=font)
        y += 60

    draw.text((W//2, 1000), "但仍低于 V9: 88 < 92", fill=(100, 100, 140), font=font, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_fullscreen_text(text, output_path):
    """全屏大字"""
    img = Image.new('RGB', (W, H), (20, 15, 15))
    draw = ImageDraw.Draw(img)
    try:
        font_big = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 56)
    except:
        font_big = ImageFont.load_default()

    lines = text.split('\n')
    y = (H - len(lines) * 80) // 2
    for line in lines:
        draw.text((W//2, y), line, fill=(255, 255, 255), font=font_big, anchor="mt")
        y += 80

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_end_card(output_path):
    """结尾卡片"""
    img = Image.new('RGB', (W, H), (8, 8, 22))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 52)
        font_small = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 28)
    except:
        font = ImageFont.load_default()
        font_small = font

    steps = ["执行", "→ 审核", "→ 修复", "→ 回归测试", "→ 稳定交付"]
    y = 400
    for step in steps:
        draw.text((W//2, y), step, fill=(0, 200, 255), font=font, anchor="mt")
        y += 80

    draw.text((W//2, 1000), "这才是真正的 AI 工作流", fill=(255, 215, 0), font=font, anchor="mt")
    draw.text((W//2, 1150), "Claude Code × Kimi 自动修复闭环", fill=(100, 100, 150), font=font_small, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


def create_final_montage(output_path):
    """最终蒙太奇"""
    img = Image.new('RGB', (W, H), (10, 10, 26))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 40)
        font_big = ImageFont.truetype("C:/Windows/Fonts/msyhbd.ttc", 52)
    except:
        font = ImageFont.load_default()
        font_big = font

    cols = [
        [("❌ 录屏失败", (200, 60, 60)), ("❌ AI 跑偏", (200, 120, 20)), ("❌ 分数崩过", (200, 80, 80))],
    ]

    y = 500
    for item, color in cols[0]:
        draw.text((W//2, y), item, fill=color, font=font, anchor="mt")
        y += 70

    draw.text((W//2, 800), "✅ 又爬回来了", fill=(100, 255, 100), font=font_big, anchor="mt")
    draw.text((W//2, 900), "这才是真正的 AI 工作流", fill=(0, 200, 255), font=font_big, anchor="mt")

    img.save(output_path, quality=95)
    print(f"  → {output_path}")


# ══════════════════════════════════════════════
# 生成所有场景帧
# ══════════════════════════════════════════════

def generate_all_scenes():
    print(f"\n{'='*60}")
    print("生成全部场景帧...")
    print(f"{'='*60}")

    frame_dir = os.path.join(CLIPS_DIR, "scene_frames")
    os.makedirs(frame_dir, exist_ok=True)

    for scene_id, start, dur, stype, params in SCENES:
        output = os.path.join(frame_dir, f"{scene_id}.png")

        if stype == "title_card":
            create_title_card(params["text"], output)
        elif stype == "terminal":
            create_terminal_screen(params["lines"], output)
        elif stype == "chatgpt_card":
            create_chatgpt_card(output)
        elif stype == "code_screen":
            create_code_screen(output)
        elif stype == "agent_cards":
            create_agent_cards(output)
        elif stype == "score_chart":
            create_score_chart(output)
        elif stype == "score_highlight":
            create_score_highlight(params["version"], params["score"], output)
        elif stype == "crash_scene":
            create_crash_scene(output)
        elif stype == "recovery_scene":
            create_recovery_scene(output)
        elif stype == "fullscreen_text":
            create_fullscreen_text(params["text"], output)
        elif stype == "rollback_scene":
            create_rollback_scene(output)
        elif stype == "portal_scene":
            create_portal_scene(output)
        elif stype == "http_verify_scene":
            create_http_verify(output)
        elif stype == "summary_cards":
            create_summary_cards(output)
        elif stype == "punch_montage":
            create_punch_montage(output)
        elif stype == "fail_list":
            create_fail_list(output)
        elif stype == "kimi_review":
            create_kimi_review(output)
        elif stype == "final_montage":
            create_final_montage(output)
        elif stype == "end_card":
            create_end_card(output)

    print(f"\n全部 {len(SCENES)} 个场景帧已生成")


# ══════════════════════════════════════════════
# FFmpeg 合成
# ══════════════════════════════════════════════

def compose_video():
    print(f"\n{'='*60}")
    print("合成最终视频...")
    print(f"{'='*60}")

    frame_dir = os.path.join(CLIPS_DIR, "scene_frames")
    audio_path = os.path.join(VO_DIR, "voiceover_master_fast.wav")
    v9_clip = os.path.join(CLIPS_DIR, "v9_extract.mp4")

    # Extract V9 video section for use in scenes (the "success" visual)
    print("\n提取 V9 视频素材...")
    subprocess.run([
        FFMPEG, "-y", "-i", V9_VIDEO,
        "-vf", "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2",
        "-t", "40", "-c:v", "libx264", "-preset", "fast", "-crf", "23",
        v9_clip
    ], capture_output=True)

    # For the actual composition, we'll create a simpler approach:
    # Create video from scene frames with proper durations using FFmpeg concat

    # Create FFmpeg concat file with each scene image + duration
    concat_file = os.path.join(CLIPS_DIR, "scenes_concat.txt")

    with open(concat_file, 'w') as f:
        for scene_id, start, dur, stype, params in SCENES:
            img_path = os.path.join(frame_dir, f"{scene_id}.png")
            f.write(f"file '{img_path}'\n")
            f.write(f"duration {dur}\n")
        # Last frame needs to be specified twice for FFmpeg
        last_scene = SCENES[-1]
        f.write(f"file '{os.path.join(frame_dir, last_scene[0])}.png'\n")

    # Generate the video from images
    raw_video = os.path.join(CLIPS_DIR, "scenes_raw.mp4")
    subprocess.run([
        FFMPEG, "-y", "-f", "concat", "-safe", "0",
        "-i", concat_file,
        "-vf", f"fps={FPS},format=yuv420p",
        "-c:v", "libx264", "-preset", "fast", "-crf", "22",
        raw_video
    ], capture_output=True)
    print(f"  → 场景视频: {raw_video}")

    # Now compose: video + audio + mixing
    # Add transitions and mix V9 clips for the success section
    final_video = os.path.join(VIDEO_DIR, "claude_code_self_evolving_video_workflow_douyin_v1.mp4")

    subprocess.run([
        FFMPEG, "-y",
        "-i", raw_video,
        "-i", audio_path,
        "-c:v", "libx264", "-preset", "medium", "-crf", "20",
        "-c:a", "aac", "-b:a", "192k",
        "-pix_fmt", "yuv420p",
        "-movflags", "+faststart",
        "-shortest",
        final_video
    ], capture_output=True)
    print(f"  → 最终视频: {final_video}")

    # Get duration
    result = subprocess.run([FFMPEG, "-i", final_video], capture_output=True, text=True)
    import re
    m = re.search(r'Duration: (\d+):(\d+):(\d+\.\d+)', result.stderr)
    if m:
        h, m, s = int(m.group(1)), int(m.group(2)), float(m.group(3))
        print(f"  → 视频时长: {m.group(1)}:{m.group(2)}:{m.group(3)} ({h*3600+m*60+s:.1f}s)")

    return final_video


# ══════════════════════════════════════════════
# 主流程
# ══════════════════════════════════════════════

if __name__ == "__main__":
    generate_all_scenes()
    final_video = compose_video()
    print(f"\n{'='*60}")
    print(f"✅ 视频合成完成: {final_video}")
    print(f"{'='*60}")
